package org.mtmi.ui.controls.midi;

import org.mtmi.io.IControlIO;
import org.mtmi.io.controls.MidiKeyboardIO;
import org.mtmi.midi.MidiManager;
import org.mtmi.ui.IAdaptable;
import org.mtmi.ui.controls.Control;
import org.mtmi.ui.controls.IValueGenerator;
import org.mtmi.ui.controls.Keyboard;
import org.mtmi.ui.controls.listeners.DisposeEvent;
import org.mtmi.ui.controls.listeners.KeyboardKeyStateEvent;
import org.mtmi.ui.controls.listeners.KeyboardListener;
import org.mtmi.ui.controls.listeners.KeyboardOctaveEvent;
import org.mtmi.ui.controls.listeners.ValueEvent;
import org.mtmi.ui.edit.IControlEditor;
import org.mtmi.ui.scenes.Scene;

import rwmidi.MidiOutput;

public class MidiKeyboard extends Keyboard 
  implements IMidiControl, IMidiInstrument, IMidiVelocity, KeyboardListener
{
	private int channel;
	private int output;
	private int velocity;
	private int instrument;
	
	private IValueGenerator ctr_instrument; //change instrument by a control
	private String ctr_instrument_connection_id;
	
	public MidiKeyboard( Scene scene, int flags ) {
		super(scene, flags);
		channel = 0;
		output = 0;
		velocity=127;
		instrument = 0;
		addKeyboardListener( this );
	}
	
	@Override
	public int getMidiChannel() { return channel; }
	
	@Override
	public void setMidiChannel( int p ) {
		channel = p;
	}
	
	@Override
	public int getMidiOutput() { return output; }
	
	@Override
	public void setMidiOutput( int o ) {
		output = o;
	}
	
	@Override
	public int getMidiInstrument() { return instrument; }
	
	@Override
	public void setMidiInstrument( int i ) {
		instrument = i;
	}
	
	@Override
	public int getMidiVelocity() { return velocity; }
	
	@Override
	public void setMidiVelocity( int i ) {
		velocity = i;
	}
	
	@Override
	public Object getAdapter( Class<?> clazz, Object arg ) {
		if( clazz == IControlEditor.class ) {
			return new MidiKeyboardControlEditor( this );
		}
		if( clazz == IControlIO.class ) {
			return new MidiKeyboardIO();
		}
		return super.getAdapter( clazz, arg );
	}

	@Override
	public void keyboardKeyStateChanged(KeyboardKeyStateEvent event) {
		MidiOutput mo = MidiManager.getInstance().getMidiOutput( output );
		mo.sendProgramChange( instrument );
		for( int i=0; i<event.count; i++ ) {
			if( event.key[i]>=0 && event.key[i]<= 127 ) {
				if( event.down[i] ) {
					mo.sendNoteOn( channel, event.key[i], velocity );
				} else {
					mo.sendNoteOff( channel, event.key[i], velocity );
				}
			}
		}
	}

	@Override
	public void keyboardOctaveCountChanged(KeyboardOctaveEvent event) {}
	
	
	/** @return the value generator connected to keyboard instrument */
	public IValueGenerator getMidiInstrumentController() { return ctr_instrument; }
	public String getMidiInstrumentControllerConnectionId() { return ctr_instrument_connection_id; }
	
	public void setMidiInstrumentController( IValueGenerator tc, String cid ) {
		if( ctr_instrument!= null ) {
			if( ctr_instrument instanceof IAdaptable ) {
				Control c = (Control)((IAdaptable)ctr_instrument).getAdapter( Control.class, null );
				c.removeControlDisposedListener( this );
			}
			ctr_instrument.removeValueChangedListener( this );
		}
		ctr_instrument = tc;
		ctr_instrument_connection_id = cid;
		if( ctr_instrument!=null ) {
			ctr_instrument.addValueChangedListener( this );
			if( ctr_instrument instanceof IAdaptable ) {
				Control c = (Control)((IAdaptable)ctr_instrument).getAdapter( Control.class, null );
				c.addControlDisposedListener( this );
			}
			updateInstrumentFromInstrumentControl();
		}
	}
	
	private void updateInstrumentFromInstrumentControl() {
		if( ctr_instrument!= null ) {
			float k = ctr_instrument.getValueF();
			//convert range to 0..127;
			int v = Math.round( 127*k );
			setMidiInstrument( v );
		}
	}
	
	@Override
	public void controlDisposed(DisposeEvent event) {
		if( event.control == ctr_instrument ) {
			ctr_instrument.removeValueChangedListener( this );
			ctr_instrument = null;
		}
		super.controlDisposed(event);
	}
	
	@Override
	public void valueChanged(ValueEvent event) {
		if( event.control == ctr_instrument ) {
			updateInstrumentFromInstrumentControl();
		}
		super.valueChanged( event );
	}
}
